﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SuperX.Common.Code
{
    public class CodeManager
    {
        public const string PythonEngineName = "IoTScriptEngine";

        private ConcurrentDictionary<string, ICode> _scriptEngines;

        public bool IsStarted { get; set; }

        public ConcurrentDictionary<string, ICode> ScriptEngines
        {
            get => _scriptEngines ?? (_scriptEngines = new ConcurrentDictionary<string, ICode>());
            private set => _scriptEngines = value;
        }

        public ConcurrentDictionary<string, DateTime> EngineLastExecuteTime = new ConcurrentDictionary<string, DateTime>();

        public ICode this[string engineName]
        {
            get
            {
                if (ScriptEngines.ContainsKey(engineName))
                {
                    return ScriptEngines[engineName];
                }
                return null;
            }
        }

        public bool TryAdd(string engineName, ICode script)
        {
            if (string.IsNullOrWhiteSpace(engineName) || script == null || ScriptEngines.ContainsKey(engineName))
            {
                return false;
            }
            return ScriptEngines.TryAdd(engineName, script);
        }

        public bool RemoveEngine(string engineName)
        {
            if (string.IsNullOrWhiteSpace(engineName))
                return false;
            var result = ScriptEngines.TryRemove(engineName, out var scriptEngine);
            scriptEngine?.Dispose();
            return result;
        }

        public void ClearEngine()
        {
            var engines = ScriptEngines.Values.ToList();
            ScriptEngines.Clear();
            foreach (var engine in engines)
            {
                engine?.Dispose();
            }
        }

        public void Start()
        {
            if (IsStarted) return;
            IsStarted = true;
            new Thread(Scan).Start();
        }

        /// <summary>
        /// 删除长时间不用的脚本引擎
        /// </summary>
        /// <param name="obj"></param>
        private void Scan(object obj)
        {
            int count = 0;
            while (IsStarted)
            {
                try
                {
                    if (count <= 300) count++;
                    else
                    {
                        count = 0;
                        var now = DateTime.Now;
                        foreach (var keyValuePair in EngineLastExecuteTime)
                        {
                            if ((now - keyValuePair.Value).TotalMinutes > 10)
                            {
                                RemoveEngine(keyValuePair.Key);
                            }
                        }
                    }
                }
                catch
                {
                }
                finally
                {
                    Thread.Sleep(1000);
                }
            }
        }

        public void Stop()
        {
            IsStarted = false;
            EngineLastExecuteTime.Clear();
        }

        public dynamic Execute(string expression, IProject project)
        {
            var id = Thread.CurrentThread.ManagedThreadId.ToString();
            if (!ScriptEngines.ContainsKey(id))
            {
                ScriptEngines.TryAdd(id, project.CreateEngine(id, null));
            }
            var result = ScriptEngines[id].Execute(expression);
            if (!EngineLastExecuteTime.ContainsKey(id))
            {
                EngineLastExecuteTime.TryAdd(id, DateTime.Now);
            }
            else EngineLastExecuteTime.TryUpdate(id, DateTime.Now, DateTime.Now);
            return result;
        }

        public TR Execute<TR>(string expression, IProject project)
        {
            var id = Thread.CurrentThread.ManagedThreadId.ToString();
            if (!ScriptEngines.ContainsKey(id))
            {
                ScriptEngines.TryAdd(id, project.CreateEngine(id, null));
            }
            dynamic result = ScriptEngines[id].Execute(expression);
            if (!EngineLastExecuteTime.ContainsKey(id))
            {
                EngineLastExecuteTime.TryAdd(id, DateTime.Now);
            }
            else EngineLastExecuteTime.TryUpdate(id, DateTime.Now, DateTime.Now);
            return result;
        }
    }
}